<!doctype html>

<html>
<head>
  <link rel="shortcut icon" href="static/images/favicon.ico" type="image/x-icon">
  <title>pastehandler.js (Closure Library API Documentation - JavaScript)</title>
  <link rel="stylesheet" href="static/css/base.css">
  <link rel="stylesheet" href="static/css/doc.css">
  <link rel="stylesheet" href="static/css/sidetree.css">
  <link rel="stylesheet" href="static/css/prettify.css">

  <script>
     var _staticFilePath = "static/";
     var _typeTreeName = "goog";
     var _fileTreeName = "Source";
  </script>

  <script src="static/js/doc.js">
  </script>


  <meta charset="utf8">
</head>

<body onload="grokdoc.onLoad();">

<div id="header">
  <div class="g-section g-tpl-50-50 g-split">
    <div class="g-unit g-first">
      <a id="logo" href="index.html">Closure Library API Documentation</a>
    </div>

    <div class="g-unit">
      <div class="g-c">
        <strong>Go to class or file:</strong>
        <input type="text" id="ac">
      </div>
    </div>
  </div>
</div>

<div class="clear"></div>

<h2><a href="local_closure_goog_events_pastehandler.js.html">pastehandler.js</a></h2>

<pre class="prettyprint lang-js">
<a name="line1"></a>// Copyright 2009 The Closure Library Authors. All Rights Reserved.
<a name="line2"></a>//
<a name="line3"></a>// Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
<a name="line4"></a>// you may not use this file except in compliance with the License.
<a name="line5"></a>// You may obtain a copy of the License at
<a name="line6"></a>//
<a name="line7"></a>//      http://www.apache.org/licenses/LICENSE-2.0
<a name="line8"></a>//
<a name="line9"></a>// Unless required by applicable law or agreed to in writing, software
<a name="line10"></a>// distributed under the License is distributed on an &quot;AS-IS&quot; BASIS,
<a name="line11"></a>// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
<a name="line12"></a>// See the License for the specific language governing permissions and
<a name="line13"></a>// limitations under the License.
<a name="line14"></a>
<a name="line15"></a>/**
<a name="line16"></a> * @fileoverview Provides a &#39;paste&#39; event detector that works consistently
<a name="line17"></a> * across different browsers.
<a name="line18"></a> *
<a name="line19"></a> * IE5, IE6, IE7, Safari3.0 and FF3.0 all fire &#39;paste&#39; events on textareas.
<a name="line20"></a> * FF2 doesn&#39;t. This class uses &#39;paste&#39; events when they are available
<a name="line21"></a> * and uses heuristics to detect the &#39;paste&#39; event when they are not available.
<a name="line22"></a> *
<a name="line23"></a> * Known issue: will not detect paste events in FF2 if you pasted exactly the
<a name="line24"></a> * same existing text.
<a name="line25"></a> * Known issue: Opera + Mac doesn&#39;t work properly because of the meta key. We
<a name="line26"></a> * can probably fix that. TODO(user): {@link KeyboardShortcutHandler} does not
<a name="line27"></a> * work either very well with opera + mac. fix that.
<a name="line28"></a> *
<a name="line29"></a> * @supported IE5, IE6, IE7, Safari3.0, Chrome, FF2.0 (linux) and FF3.0 and
<a name="line30"></a> * Opera (mac and windows).
<a name="line31"></a> *
<a name="line32"></a> * @see ../demos/pastehandler.html
<a name="line33"></a> */
<a name="line34"></a>
<a name="line35"></a>goog.provide(&#39;goog.events.PasteHandler&#39;);
<a name="line36"></a>goog.provide(&#39;goog.events.PasteHandler.EventType&#39;);
<a name="line37"></a>goog.provide(&#39;goog.events.PasteHandler.State&#39;);
<a name="line38"></a>
<a name="line39"></a>goog.require(&#39;goog.Timer&#39;);
<a name="line40"></a>goog.require(&#39;goog.async.ConditionalDelay&#39;);
<a name="line41"></a>goog.require(&#39;goog.events.BrowserEvent&#39;);
<a name="line42"></a>goog.require(&#39;goog.events.EventHandler&#39;);
<a name="line43"></a>goog.require(&#39;goog.events.EventTarget&#39;);
<a name="line44"></a>goog.require(&#39;goog.events.EventType&#39;);
<a name="line45"></a>goog.require(&#39;goog.events.KeyCodes&#39;);
<a name="line46"></a>goog.require(&#39;goog.log&#39;);
<a name="line47"></a>goog.require(&#39;goog.userAgent&#39;);
<a name="line48"></a>
<a name="line49"></a>
<a name="line50"></a>
<a name="line51"></a>/**
<a name="line52"></a> * A paste event detector. Gets an {@code element} as parameter and fires
<a name="line53"></a> * {@code goog.events.PasteHandler.EventType.PASTE} events when text is
<a name="line54"></a> * pasted in the {@code element}. Uses heuristics to detect paste events in FF2.
<a name="line55"></a> * See more details of the heuristic on {@link #handleEvent_}.
<a name="line56"></a> *
<a name="line57"></a> * @param {Element} element The textarea element we are listening on.
<a name="line58"></a> * @constructor
<a name="line59"></a> * @extends {goog.events.EventTarget}
<a name="line60"></a> */
<a name="line61"></a>goog.events.PasteHandler = function(element) {
<a name="line62"></a>  goog.events.EventTarget.call(this);
<a name="line63"></a>
<a name="line64"></a>  /**
<a name="line65"></a>   * The element that you want to listen for paste events on.
<a name="line66"></a>   * @type {Element}
<a name="line67"></a>   * @private
<a name="line68"></a>   */
<a name="line69"></a>  this.element_ = element;
<a name="line70"></a>
<a name="line71"></a>  /**
<a name="line72"></a>   * The last known value of the element. Kept to check if things changed. See
<a name="line73"></a>   * more details on {@link #handleEvent_}.
<a name="line74"></a>   * @type {string}
<a name="line75"></a>   * @private
<a name="line76"></a>   */
<a name="line77"></a>  this.oldValue_ = this.element_.value;
<a name="line78"></a>
<a name="line79"></a>  /**
<a name="line80"></a>   * Handler for events.
<a name="line81"></a>   * @type {goog.events.EventHandler.&lt;!goog.events.PasteHandler&gt;}
<a name="line82"></a>   * @private
<a name="line83"></a>   */
<a name="line84"></a>  this.eventHandler_ = new goog.events.EventHandler(this);
<a name="line85"></a>
<a name="line86"></a>  /**
<a name="line87"></a>   * The last time an event occurred on the element. Kept to check whether the
<a name="line88"></a>   * last event was generated by two input events or by multiple fast key events
<a name="line89"></a>   * that got swallowed. See more details on {@link #handleEvent_}.
<a name="line90"></a>   * @type {number}
<a name="line91"></a>   * @private
<a name="line92"></a>   */
<a name="line93"></a>  this.lastTime_ = goog.now();
<a name="line94"></a>
<a name="line95"></a>  if (goog.userAgent.WEBKIT ||
<a name="line96"></a>      goog.userAgent.IE ||
<a name="line97"></a>      goog.userAgent.GECKO &amp;&amp; goog.userAgent.isVersionOrHigher(&#39;1.9&#39;)) {
<a name="line98"></a>    // Most modern browsers support the paste event.
<a name="line99"></a>    this.eventHandler_.listen(element, goog.events.EventType.PASTE,
<a name="line100"></a>        this.dispatch_);
<a name="line101"></a>  } else {
<a name="line102"></a>    // But FF2 and Opera doesn&#39;t. we listen for a series of events to try to
<a name="line103"></a>    // find out if a paste occurred. We enumerate and cover all known ways to
<a name="line104"></a>    // paste text on textareas.  See more details on {@link #handleEvent_}.
<a name="line105"></a>    var events = [
<a name="line106"></a>      goog.events.EventType.KEYDOWN,
<a name="line107"></a>      goog.events.EventType.BLUR,
<a name="line108"></a>      goog.events.EventType.FOCUS,
<a name="line109"></a>      goog.events.EventType.MOUSEOVER,
<a name="line110"></a>      &#39;input&#39;
<a name="line111"></a>    ];
<a name="line112"></a>    this.eventHandler_.listen(element, events, this.handleEvent_);
<a name="line113"></a>  }
<a name="line114"></a>
<a name="line115"></a>  /**
<a name="line116"></a>   * ConditionalDelay used to poll for changes in the text element once users
<a name="line117"></a>   * paste text. Browsers fire paste events BEFORE the text is actually present
<a name="line118"></a>   * in the element.value property.
<a name="line119"></a>   * @type {goog.async.ConditionalDelay}
<a name="line120"></a>   * @private
<a name="line121"></a>   */
<a name="line122"></a>  this.delay_ = new goog.async.ConditionalDelay(
<a name="line123"></a>      goog.bind(this.checkUpdatedText_, this));
<a name="line124"></a>
<a name="line125"></a>};
<a name="line126"></a>goog.inherits(goog.events.PasteHandler, goog.events.EventTarget);
<a name="line127"></a>
<a name="line128"></a>
<a name="line129"></a>/**
<a name="line130"></a> * The types of events fired by this class.
<a name="line131"></a> * @enum {string}
<a name="line132"></a> */
<a name="line133"></a>goog.events.PasteHandler.EventType = {
<a name="line134"></a>  /**
<a name="line135"></a>   * Dispatched as soon as the paste event is detected, but before the pasted
<a name="line136"></a>   * text has been added to the text element we&#39;re listening to.
<a name="line137"></a>   */
<a name="line138"></a>  PASTE: &#39;paste&#39;,
<a name="line139"></a>
<a name="line140"></a>  /**
<a name="line141"></a>   * Dispatched after detecting a change to the value of text element
<a name="line142"></a>   * (within 200msec of receiving the PASTE event).
<a name="line143"></a>   */
<a name="line144"></a>  AFTER_PASTE: &#39;after_paste&#39;
<a name="line145"></a>};
<a name="line146"></a>
<a name="line147"></a>
<a name="line148"></a>/**
<a name="line149"></a> * The mandatory delay we expect between two {@code input} events, used to
<a name="line150"></a> * differentiated between non key paste events and key events.
<a name="line151"></a> * @type {number}
<a name="line152"></a> */
<a name="line153"></a>goog.events.PasteHandler.MANDATORY_MS_BETWEEN_INPUT_EVENTS_TIE_BREAKER =
<a name="line154"></a>    400;
<a name="line155"></a>
<a name="line156"></a>
<a name="line157"></a>/**
<a name="line158"></a> * The period between each time we check whether the pasted text appears in the
<a name="line159"></a> * text element or not.
<a name="line160"></a> * @type {number}
<a name="line161"></a> * @private
<a name="line162"></a> */
<a name="line163"></a>goog.events.PasteHandler.PASTE_POLLING_PERIOD_MS_ = 50;
<a name="line164"></a>
<a name="line165"></a>
<a name="line166"></a>/**
<a name="line167"></a> * The maximum amount of time we want to poll for changes.
<a name="line168"></a> * @type {number}
<a name="line169"></a> * @private
<a name="line170"></a> */
<a name="line171"></a>goog.events.PasteHandler.PASTE_POLLING_TIMEOUT_MS_ = 200;
<a name="line172"></a>
<a name="line173"></a>
<a name="line174"></a>/**
<a name="line175"></a> * The states that this class can be found, on the paste detection algorithm.
<a name="line176"></a> * @enum {string}
<a name="line177"></a> */
<a name="line178"></a>goog.events.PasteHandler.State = {
<a name="line179"></a>  INIT: &#39;init&#39;,
<a name="line180"></a>  FOCUSED: &#39;focused&#39;,
<a name="line181"></a>  TYPING: &#39;typing&#39;
<a name="line182"></a>};
<a name="line183"></a>
<a name="line184"></a>
<a name="line185"></a>/**
<a name="line186"></a> * The initial state of the paste detection algorithm.
<a name="line187"></a> * @type {goog.events.PasteHandler.State}
<a name="line188"></a> * @private
<a name="line189"></a> */
<a name="line190"></a>goog.events.PasteHandler.prototype.state_ =
<a name="line191"></a>    goog.events.PasteHandler.State.INIT;
<a name="line192"></a>
<a name="line193"></a>
<a name="line194"></a>/**
<a name="line195"></a> * The previous event that caused us to be on the current state.
<a name="line196"></a> * @type {?string}
<a name="line197"></a> * @private
<a name="line198"></a> */
<a name="line199"></a>goog.events.PasteHandler.prototype.previousEvent_;
<a name="line200"></a>
<a name="line201"></a>
<a name="line202"></a>/**
<a name="line203"></a> * A logger, used to help us debug the algorithm.
<a name="line204"></a> * @type {goog.log.Logger}
<a name="line205"></a> * @private
<a name="line206"></a> */
<a name="line207"></a>goog.events.PasteHandler.prototype.logger_ =
<a name="line208"></a>    goog.log.getLogger(&#39;goog.events.PasteHandler&#39;);
<a name="line209"></a>
<a name="line210"></a>
<a name="line211"></a>/** @override */
<a name="line212"></a>goog.events.PasteHandler.prototype.disposeInternal = function() {
<a name="line213"></a>  goog.events.PasteHandler.superClass_.disposeInternal.call(this);
<a name="line214"></a>  this.eventHandler_.dispose();
<a name="line215"></a>  this.eventHandler_ = null;
<a name="line216"></a>  this.delay_.dispose();
<a name="line217"></a>  this.delay_ = null;
<a name="line218"></a>};
<a name="line219"></a>
<a name="line220"></a>
<a name="line221"></a>/**
<a name="line222"></a> * Returns the current state of the paste detection algorithm. Used mostly for
<a name="line223"></a> * testing.
<a name="line224"></a> * @return {goog.events.PasteHandler.State} The current state of the class.
<a name="line225"></a> */
<a name="line226"></a>goog.events.PasteHandler.prototype.getState = function() {
<a name="line227"></a>  return this.state_;
<a name="line228"></a>};
<a name="line229"></a>
<a name="line230"></a>
<a name="line231"></a>/**
<a name="line232"></a> * Returns the event handler.
<a name="line233"></a> * @return {goog.events.EventHandler.&lt;T&gt;} The event handler.
<a name="line234"></a> * @protected
<a name="line235"></a> * @this T
<a name="line236"></a> * @template T
<a name="line237"></a> */
<a name="line238"></a>goog.events.PasteHandler.prototype.getEventHandler = function() {
<a name="line239"></a>  return this.eventHandler_;
<a name="line240"></a>};
<a name="line241"></a>
<a name="line242"></a>
<a name="line243"></a>/**
<a name="line244"></a> * Checks whether the element.value property was updated, and if so, dispatches
<a name="line245"></a> * the event that let clients know that the text is available.
<a name="line246"></a> * @return {boolean} Whether the polling should stop or not, based on whether
<a name="line247"></a> *     we found a text change or not.
<a name="line248"></a> * @private
<a name="line249"></a> */
<a name="line250"></a>goog.events.PasteHandler.prototype.checkUpdatedText_ = function() {
<a name="line251"></a>  if (this.oldValue_ == this.element_.value) {
<a name="line252"></a>    return false;
<a name="line253"></a>  }
<a name="line254"></a>  goog.log.info(this.logger_, &#39;detected textchange after paste&#39;);
<a name="line255"></a>  this.dispatchEvent(goog.events.PasteHandler.EventType.AFTER_PASTE);
<a name="line256"></a>  return true;
<a name="line257"></a>};
<a name="line258"></a>
<a name="line259"></a>
<a name="line260"></a>/**
<a name="line261"></a> * Dispatches the paste event.
<a name="line262"></a> * @param {goog.events.BrowserEvent} e The underlying browser event.
<a name="line263"></a> * @private
<a name="line264"></a> */
<a name="line265"></a>goog.events.PasteHandler.prototype.dispatch_ = function(e) {
<a name="line266"></a>  var event = new goog.events.BrowserEvent(e.getBrowserEvent());
<a name="line267"></a>  event.type = goog.events.PasteHandler.EventType.PASTE;
<a name="line268"></a>  this.dispatchEvent(event);
<a name="line269"></a>
<a name="line270"></a>  // Starts polling for updates in the element.value property so we can tell
<a name="line271"></a>  // when do dispatch the AFTER_PASTE event. (We do an initial check after an
<a name="line272"></a>  // async delay of 0 msec since some browsers update the text right away and
<a name="line273"></a>  // our poller will always wait one period before checking).
<a name="line274"></a>  goog.Timer.callOnce(function() {
<a name="line275"></a>    if (!this.checkUpdatedText_()) {
<a name="line276"></a>      this.delay_.start(
<a name="line277"></a>          goog.events.PasteHandler.PASTE_POLLING_PERIOD_MS_,
<a name="line278"></a>          goog.events.PasteHandler.PASTE_POLLING_TIMEOUT_MS_);
<a name="line279"></a>    }
<a name="line280"></a>  }, 0, this);
<a name="line281"></a>};
<a name="line282"></a>
<a name="line283"></a>
<a name="line284"></a>/**
<a name="line285"></a> * The main event handler which implements a state machine.
<a name="line286"></a> *
<a name="line287"></a> * To handle FF2, we enumerate and cover all the known ways a user can paste:
<a name="line288"></a> *
<a name="line289"></a> * 1) ctrl+v, shift+insert, cmd+v
<a name="line290"></a> * 2) right click -&gt; paste
<a name="line291"></a> * 3) edit menu -&gt; paste
<a name="line292"></a> * 4) drag and drop
<a name="line293"></a> * 5) middle click
<a name="line294"></a> *
<a name="line295"></a> * (1) is easy and can be detected by listening for key events and finding out
<a name="line296"></a> * which keys are pressed. (2), (3), (4) and (5) do not generate a key event,
<a name="line297"></a> * so we need to listen for more than that. (2-5) all generate &#39;input&#39; events,
<a name="line298"></a> * but so does key events. So we need to have some sort of &#39;how did the input
<a name="line299"></a> * event was generated&#39; history algorithm.
<a name="line300"></a> *
<a name="line301"></a> * (2) is an interesting case in Opera on a Mac: since Macs does not have two
<a name="line302"></a> * buttons, right clicking involves pressing the CTRL key. Even more interesting
<a name="line303"></a> * is the fact that opera does NOT set the e.ctrlKey bit. Instead, it sets
<a name="line304"></a> * e.keyCode = 0.
<a name="line305"></a> * {@link http://www.quirksmode.org/js/keys.html}
<a name="line306"></a> *
<a name="line307"></a> * (1) is also an interesting case in Opera on a Mac: Opera is the only browser
<a name="line308"></a> * covered by this class that can detect the cmd key (FF2 can&#39;t apparently). And
<a name="line309"></a> * it fires e.keyCode = 17, which is the CTRL key code.
<a name="line310"></a> * {@link http://www.quirksmode.org/js/keys.html}
<a name="line311"></a> *
<a name="line312"></a> * NOTE(user, pbarry): There is an interesting thing about (5): on Linux, (5)
<a name="line313"></a> * pastes the last thing that you highlighted, not the last thing that you
<a name="line314"></a> * ctrl+c&#39;ed. This code will still generate a {@code PASTE} event though.
<a name="line315"></a> *
<a name="line316"></a> * We enumerate all the possible steps a user can take to paste text and we
<a name="line317"></a> * implemented the transition between the steps in a state machine. The
<a name="line318"></a> * following is the design of the state machine:
<a name="line319"></a> *
<a name="line320"></a> * matching paths:
<a name="line321"></a> *
<a name="line322"></a> * (1) happens on INIT -&gt; FOCUSED -&gt; TYPING -&gt; [e.ctrlKey &amp; e.keyCode = &#39;v&#39;]
<a name="line323"></a> * (2-3) happens on INIT -&gt; FOCUSED -&gt; [input event happened]
<a name="line324"></a> * (4) happens on INIT -&gt; [mouseover &amp;&amp; text changed]
<a name="line325"></a> *
<a name="line326"></a> * non matching paths:
<a name="line327"></a> *
<a name="line328"></a> * user is typing normally
<a name="line329"></a> * INIT -&gt; FOCUS -&gt; TYPING -&gt; INPUT -&gt; INIT
<a name="line330"></a> *
<a name="line331"></a> * @param {goog.events.BrowserEvent} e The underlying browser event.
<a name="line332"></a> * @private
<a name="line333"></a> */
<a name="line334"></a>goog.events.PasteHandler.prototype.handleEvent_ = function(e) {
<a name="line335"></a>  // transition between states happen at each browser event, and depend on the
<a name="line336"></a>  // current state, the event that led to this state, and the event input.
<a name="line337"></a>  switch (this.state_) {
<a name="line338"></a>    case goog.events.PasteHandler.State.INIT: {
<a name="line339"></a>      this.handleUnderInit_(e);
<a name="line340"></a>      break;
<a name="line341"></a>    }
<a name="line342"></a>    case goog.events.PasteHandler.State.FOCUSED: {
<a name="line343"></a>      this.handleUnderFocused_(e);
<a name="line344"></a>      break;
<a name="line345"></a>    }
<a name="line346"></a>    case goog.events.PasteHandler.State.TYPING: {
<a name="line347"></a>      this.handleUnderTyping_(e);
<a name="line348"></a>      break;
<a name="line349"></a>    }
<a name="line350"></a>    default: {
<a name="line351"></a>      goog.log.error(this.logger_, &#39;invalid &#39; + this.state_ + &#39; state&#39;);
<a name="line352"></a>    }
<a name="line353"></a>  }
<a name="line354"></a>  this.lastTime_ = goog.now();
<a name="line355"></a>  this.oldValue_ = this.element_.value;
<a name="line356"></a>  goog.log.info(this.logger_, e.type + &#39; -&gt; &#39; + this.state_);
<a name="line357"></a>  this.previousEvent_ = e.type;
<a name="line358"></a>};
<a name="line359"></a>
<a name="line360"></a>
<a name="line361"></a>/**
<a name="line362"></a> * {@code goog.events.PasteHandler.EventType.INIT} is the first initial state
<a name="line363"></a> * the textarea is found. You can only leave this state by setting focus on the
<a name="line364"></a> * textarea, which is how users will input text. You can also paste things using
<a name="line365"></a> * drag and drop, which will not generate a {@code goog.events.EventType.FOCUS}
<a name="line366"></a> * event, but will generate a {@code goog.events.EventType.MOUSEOVER}.
<a name="line367"></a> *
<a name="line368"></a> * For browsers that support the &#39;paste&#39; event, we match it and stay on the same
<a name="line369"></a> * state.
<a name="line370"></a> *
<a name="line371"></a> * @param {goog.events.BrowserEvent} e The underlying browser event.
<a name="line372"></a> * @private
<a name="line373"></a> */
<a name="line374"></a>goog.events.PasteHandler.prototype.handleUnderInit_ = function(e) {
<a name="line375"></a>  switch (e.type) {
<a name="line376"></a>    case goog.events.EventType.BLUR: {
<a name="line377"></a>      this.state_ = goog.events.PasteHandler.State.INIT;
<a name="line378"></a>      break;
<a name="line379"></a>    }
<a name="line380"></a>    case goog.events.EventType.FOCUS: {
<a name="line381"></a>      this.state_ = goog.events.PasteHandler.State.FOCUSED;
<a name="line382"></a>      break;
<a name="line383"></a>    }
<a name="line384"></a>    case goog.events.EventType.MOUSEOVER: {
<a name="line385"></a>      this.state_ = goog.events.PasteHandler.State.INIT;
<a name="line386"></a>      if (this.element_.value != this.oldValue_) {
<a name="line387"></a>        goog.log.info(this.logger_, &#39;paste by dragdrop while on init!&#39;);
<a name="line388"></a>        this.dispatch_(e);
<a name="line389"></a>      }
<a name="line390"></a>      break;
<a name="line391"></a>    }
<a name="line392"></a>    default: {
<a name="line393"></a>      goog.log.error(this.logger_,
<a name="line394"></a>          &#39;unexpected event &#39; + e.type + &#39;during init&#39;);
<a name="line395"></a>    }
<a name="line396"></a>  }
<a name="line397"></a>};
<a name="line398"></a>
<a name="line399"></a>
<a name="line400"></a>/**
<a name="line401"></a> * {@code goog.events.PasteHandler.EventType.FOCUSED} is typically the second
<a name="line402"></a> * state the textarea will be, which is followed by the {@code INIT} state. On
<a name="line403"></a> * this state, users can paste in three different ways: edit -&gt; paste,
<a name="line404"></a> * right click -&gt; paste and drag and drop.
<a name="line405"></a> *
<a name="line406"></a> * The latter will generate a {@code goog.events.EventType.MOUSEOVER} event,
<a name="line407"></a> * which we match by making sure the textarea text changed. The first two will
<a name="line408"></a> * generate an &#39;input&#39;, which we match by making sure it was NOT generated by a
<a name="line409"></a> * key event (which also generates an &#39;input&#39; event).
<a name="line410"></a> *
<a name="line411"></a> * Unfortunately, in Firefox, if you type fast, some KEYDOWN events are
<a name="line412"></a> * swallowed but an INPUT event may still happen. That means we need to
<a name="line413"></a> * differentiate between two consecutive INPUT events being generated either by
<a name="line414"></a> * swallowed key events OR by a valid edit -&gt; paste -&gt; edit -&gt; paste action. We
<a name="line415"></a> * do this by checking a minimum time between the two events. This heuristic
<a name="line416"></a> * seems to work well, but it is obviously a heuristic :).
<a name="line417"></a> *
<a name="line418"></a> * @param {goog.events.BrowserEvent} e The underlying browser event.
<a name="line419"></a> * @private
<a name="line420"></a> */
<a name="line421"></a>goog.events.PasteHandler.prototype.handleUnderFocused_ = function(e) {
<a name="line422"></a>  switch (e.type) {
<a name="line423"></a>    case &#39;input&#39; : {
<a name="line424"></a>      // there are two different events that happen in practice that involves
<a name="line425"></a>      // consecutive &#39;input&#39; events. we use a heuristic to differentiate
<a name="line426"></a>      // between the one that generates a valid paste action and the one that
<a name="line427"></a>      // doesn&#39;t.
<a name="line428"></a>      // @see testTypingReallyFastDispatchesTwoInputEventsBeforeTheKEYDOWNEvent
<a name="line429"></a>      // and
<a name="line430"></a>      // @see testRightClickRightClickAlsoDispatchesTwoConsecutiveInputEvents
<a name="line431"></a>      // Notice that an &#39;input&#39; event may be also triggered by a &#39;middle click&#39;
<a name="line432"></a>      // paste event, which is described in
<a name="line433"></a>      // @see testMiddleClickWithoutFocusTriggersPasteEvent
<a name="line434"></a>      var minimumMilisecondsBetweenInputEvents = this.lastTime_ +
<a name="line435"></a>          goog.events.PasteHandler.
<a name="line436"></a>              MANDATORY_MS_BETWEEN_INPUT_EVENTS_TIE_BREAKER;
<a name="line437"></a>      if (goog.now() &gt; minimumMilisecondsBetweenInputEvents ||
<a name="line438"></a>          this.previousEvent_ == goog.events.EventType.FOCUS) {
<a name="line439"></a>        goog.log.info(this.logger_, &#39;paste by textchange while focused!&#39;);
<a name="line440"></a>        this.dispatch_(e);
<a name="line441"></a>      }
<a name="line442"></a>      break;
<a name="line443"></a>    }
<a name="line444"></a>    case goog.events.EventType.BLUR: {
<a name="line445"></a>      this.state_ = goog.events.PasteHandler.State.INIT;
<a name="line446"></a>      break;
<a name="line447"></a>    }
<a name="line448"></a>    case goog.events.EventType.KEYDOWN: {
<a name="line449"></a>      goog.log.info(this.logger_, &#39;key down ... looking for ctrl+v&#39;);
<a name="line450"></a>      // Opera + MAC does not set e.ctrlKey. Instead, it gives me e.keyCode = 0.
<a name="line451"></a>      // http://www.quirksmode.org/js/keys.html
<a name="line452"></a>      if (goog.userAgent.MAC &amp;&amp; goog.userAgent.OPERA &amp;&amp; e.keyCode == 0 ||
<a name="line453"></a>          goog.userAgent.MAC &amp;&amp; goog.userAgent.OPERA &amp;&amp; e.keyCode == 17) {
<a name="line454"></a>        break;
<a name="line455"></a>      }
<a name="line456"></a>      this.state_ = goog.events.PasteHandler.State.TYPING;
<a name="line457"></a>      break;
<a name="line458"></a>    }
<a name="line459"></a>    case goog.events.EventType.MOUSEOVER: {
<a name="line460"></a>      if (this.element_.value != this.oldValue_) {
<a name="line461"></a>        goog.log.info(this.logger_, &#39;paste by dragdrop while focused!&#39;);
<a name="line462"></a>        this.dispatch_(e);
<a name="line463"></a>      }
<a name="line464"></a>      break;
<a name="line465"></a>    }
<a name="line466"></a>    default: {
<a name="line467"></a>      goog.log.error(this.logger_,
<a name="line468"></a>          &#39;unexpected event &#39; + e.type + &#39; during focused&#39;);
<a name="line469"></a>    }
<a name="line470"></a>  }
<a name="line471"></a>};
<a name="line472"></a>
<a name="line473"></a>
<a name="line474"></a>/**
<a name="line475"></a> * {@code goog.events.PasteHandler.EventType.TYPING} is the third state
<a name="line476"></a> * this class can be. It exists because each KEYPRESS event will ALSO generate
<a name="line477"></a> * an INPUT event (because the textarea value changes), and we need to
<a name="line478"></a> * differentiate between an INPUT event generated by a key event and an INPUT
<a name="line479"></a> * event generated by edit -&gt; paste actions.
<a name="line480"></a> *
<a name="line481"></a> * This is the state that we match the ctrl+v pattern.
<a name="line482"></a> *
<a name="line483"></a> * @param {goog.events.BrowserEvent} e The underlying browser event.
<a name="line484"></a> * @private
<a name="line485"></a> */
<a name="line486"></a>goog.events.PasteHandler.prototype.handleUnderTyping_ = function(e) {
<a name="line487"></a>  switch (e.type) {
<a name="line488"></a>    case &#39;input&#39; : {
<a name="line489"></a>      this.state_ = goog.events.PasteHandler.State.FOCUSED;
<a name="line490"></a>      break;
<a name="line491"></a>    }
<a name="line492"></a>    case goog.events.EventType.BLUR: {
<a name="line493"></a>      this.state_ = goog.events.PasteHandler.State.INIT;
<a name="line494"></a>      break;
<a name="line495"></a>    }
<a name="line496"></a>    case goog.events.EventType.KEYDOWN: {
<a name="line497"></a>      if (e.ctrlKey &amp;&amp; e.keyCode == goog.events.KeyCodes.V ||
<a name="line498"></a>          e.shiftKey &amp;&amp; e.keyCode == goog.events.KeyCodes.INSERT ||
<a name="line499"></a>          e.metaKey &amp;&amp; e.keyCode == goog.events.KeyCodes.V) {
<a name="line500"></a>        goog.log.info(this.logger_, &#39;paste by ctrl+v while keypressed!&#39;);
<a name="line501"></a>        this.dispatch_(e);
<a name="line502"></a>      }
<a name="line503"></a>      break;
<a name="line504"></a>    }
<a name="line505"></a>    case goog.events.EventType.MOUSEOVER: {
<a name="line506"></a>      if (this.element_.value != this.oldValue_) {
<a name="line507"></a>        goog.log.info(this.logger_, &#39;paste by dragdrop while keypressed!&#39;);
<a name="line508"></a>        this.dispatch_(e);
<a name="line509"></a>      }
<a name="line510"></a>      break;
<a name="line511"></a>    }
<a name="line512"></a>    default: {
<a name="line513"></a>      goog.log.error(this.logger_,
<a name="line514"></a>          &#39;unexpected event &#39; + e.type + &#39; during keypressed&#39;);
<a name="line515"></a>    }
<a name="line516"></a>  }
<a name="line517"></a>};
</pre>


</body>
</html>
